#include "positionwidget.h"
#include "ui_positionwidget.h"

PositionWidget::PositionWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::PositionWidget)
{
    // TODO: dysplay insturment details

    xmlParser = XmlPositionParser();
    inswid = new InstrumentInfo(instrument);

    // Initial setup
    ui->setupUi(this);

    interpret_as_real_note = false;
    display_real_note_SBN = false;
    display_real_note_SBP = false;

    buildWidgets(); // Fill combo box etc
    setupConnections(); //Setup all connections (static) TODO: setupConnections
    setupWidgets(); // Basic setup of widgets
    rebuildComboTree(); // Build for first time combo tree

    // Initial update of everithing
    updateAll();
}

PositionWidget::~PositionWidget()
{
    delete ui;
}

void PositionWidget::buildWidgets(void) {
    ui->pitchBaseClassCombo->clear();
    ui->pitchBaseClassCombo->addItems(PitchNoteList());
    ui->pitchBaseClassCombo->setCurrentIndex(PitchNote().toInt());

    ui->pitchAltClassCombo->setCurrentIndex(AlterNote().toUnsignedInt());

    ui->pitchOctaveCombo->clear();
    ui->pitchOctaveCombo->addItems(OctaveList());
    ui->pitchOctaveCombo->setCurrentIndex(Octave().toUnsignedInt());

    ui->temp_combo->clear();
    ui->temp_combo->addItems(TemperamentList());
}

void PositionWidget::setupConnections(void) {
    connect(ui->pitchBaseClassCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onNoteChanged()));
    connect(ui->pitchAltClassCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onNoteChanged()));
    connect(ui->pitchOctaveCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onNoteChanged()));
    connect(ui->clefButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(onClefChanged()));

    connect(ui->class_combo, SIGNAL(activated(int)), this, SLOT(onInstrumentClassChanged()));
    connect(ui->type_combo, SIGNAL(activated(int)), this, SLOT(onInstrumentTypeChanged()));
    connect(ui->instrument_combo, SIGNAL(activated(int)), this, SLOT(onInstrumentNameChanged()));
    connect(ui->tonality_combo, SIGNAL(activated(int)), this, SLOT(onInstrumentTonalityChanged()));

    // Plus/Minus Buttons for pos
    connect(ui->next_pos_button, SIGNAL(clicked()), this, SLOT(nextPos()));
    connect(ui->prev_pos_button, SIGNAL(clicked()), this, SLOT(prevPos()));
    // Plus/Minus Buttons for note
    connect(ui->next_note_button, SIGNAL(clicked()), this, SLOT(nextNote()));
    connect(ui->prev_note_button, SIGNAL(clicked()), this, SLOT(prevNote()));

    // Plus/Minus Buttons for picth/alt/oct
    connect(ui->minus_pitch_button, SIGNAL(clicked()), this, SLOT(prevPitch()));
    connect(ui->plus_pitch_button, SIGNAL(clicked()), this, SLOT(nextPitch()));
    connect(ui->minus_alt_button, SIGNAL(clicked()), this, SLOT(prevAlt()));
    connect(ui->plus_alt_button, SIGNAL(clicked()), this, SLOT(nextAlt()));
    connect(ui->minus_oct_button, SIGNAL(clicked()), this, SLOT(prevOct()));
    connect(ui->plus_oct_button, SIGNAL(clicked()), this, SLOT(nextOct()));

    // Combo for positions
    connect(ui->positions_list_combo, SIGNAL(currentIndexChanged(QString)), this, SLOT(updateSearchByPosition()));

    // Transposition toggles
    connect(ui->display_real_SBP_toggle, SIGNAL(toggled(bool)), this, SLOT(setDisplayRealSBP(bool)));
    connect(ui->display_real_SBN_toggle, SIGNAL(toggled(bool)), this, SLOT(setDisplayRealSBN(bool)));
    connect(ui->interpret_as_real_toggle, SIGNAL(toggled(bool)), this, SLOT(setInterpretAsReal(bool)));

    // Info window
    connect(ui->instrument_info_button, SIGNAL(clicked()), this, SLOT(onInstrumentButtonClicked()));
}

void PositionWidget::setupWidgets(void) {
    ui->advanced_checkbox->setChecked(false);
    ui->advanced_frame->hide();
    ui->warning_frame->hide();
}

void PositionWidget::rebuildComboTree(QString className, QString instrumentName, QString typeName, QString tonalityName) {
    QList<QStringList> tree = xmlParser.getTree(className, instrumentName, typeName, tonalityName);
    //qDebug() << tree[0] << tree[1] << tree[2] << tree[3];
    if (className == "") {
        ui->class_combo->clear();
        ui->class_combo->addItems(tree[0]);
    }
    if (instrumentName == "") {
        ui->instrument_combo->clear();
        ui->instrument_combo->addItems(tree[1]);
    }
    if (typeName == "") {
        ui->type_combo->clear();
        ui->type_combo->addItems(tree[2]);
    }
    if (tonalityName == "") {
        ui->tonality_combo->clear();
        ui->tonality_combo->addItems(tree[3]);
    }
}

void PositionWidget::updateInstrument(void)
{
    // Get instrument details
    QString cls = ui->class_combo->currentText();
    QString ins = ui->instrument_combo->currentText();
    QString typ = ui->type_combo->currentText();
    QString ton = ui->tonality_combo->currentText();

    // Create new instrument from names (contains everithing)
    instrument = xmlParser.getInstrument(cls, ins, typ, ton);


    // Handle default tonality (basenote) of instrument
    //qDebug() << instrument.getTonality().toPitchNote().toInt() << instrument.getTonality().toAlterNote().toInt();
    ui->pitchBaseClassCombo->setCurrentIndex(instrument.getTonality().toPitchNote().toInt());
    ui->pitchAltClassCombo->setCurrentIndex(instrument.getTonality().toAlterNote().toUnsignedInt());

    // Populate positions
    ui->positions_list_combo->clear();
    QList < BasePosition> base_pos_list = instrument.getBasePositions();
    for(int i = 0; i < base_pos_list.length(); i++) {
        BasePosition bp = base_pos_list.at(i);
        ui->positions_list_combo->addItem(bp.getName());
    }

    // Handle default clef of instrument
    Clef clf = instrument.getClef();
    if(clf == Clef("treble")) ui->treble_radio_button->toggle();
    else if(clf == Clef("tenor")) ui->tenor_radio_button->toggle();
    else if(clf == Clef("bass")) ui->bass_radio_button->toggle();

    // Get octave
    ui->pitchOctaveCombo->setCurrentIndex(instrument.getOctave().toUnsignedInt());

    // Check if instrument is transposing
    if(instrument.isTransposingInstrument()) {
        ui->display_real_SBN_toggle->setEnabled(true);
        ui->display_real_SBP_toggle->setEnabled(true);
        ui->interpret_as_real_toggle->setEnabled(true);
        ui->display_real_SBN_toggle->setCheckable(true);
        ui->display_real_SBP_toggle->setCheckable(true);
        ui->interpret_as_real_toggle->setCheckable(true);
    } else {
        ui->display_real_SBN_toggle->setEnabled(false);
        ui->display_real_SBP_toggle->setEnabled(false);
        ui->interpret_as_real_toggle->setEnabled(false);
        ui->display_real_SBN_toggle->setCheckable(false);
        ui->display_real_SBP_toggle->setCheckable(false);
        ui->interpret_as_real_toggle->setCheckable(false);
    }

    // Update
    onClefChanged();

    // Update Instrument on widget
    inswid->setInstrument(instrument);
}

void PositionWidget::updateNote(void)
{
    // Update note and note pixmax
    int nt = ui->pitchBaseClassCombo->currentIndex();
    int alt = ui->pitchAltClassCombo->currentIndex();
    int oct = ui->pitchOctaveCombo->currentIndex();

    // Recreate note
    Note old_note = Note(nt, alt, oct, true, true);
    Note transposed_note = instrument.getRealNote(old_note); // from not real to real note
    Note new_note = instrument.getReadedNote(old_note); // from real note to not real note
    QString string = tr("<b>%1</b> (real note: <b>%2</b>)");
    QString string2 = tr("<b>%1</b>");
    if(transposed_note == old_note) {
        current_note = old_note;
        ui->conversion_info_label->setText(string2.arg(current_note.toQString()));
        //ui->conversion_info_label->show();
    } else if(interpret_as_real_note) {
        ui->conversion_info_label->setText(string.arg(new_note.toQString()).arg(old_note.toQString()));
        //ui->conversion_info_label->show();
        current_note = old_note;
    } else {
        ui->conversion_info_label->setText(string.arg(old_note.toQString()).arg(transposed_note.toQString()));
        //ui->conversion_info_label->show();
        current_note = transposed_note;
    }

    // Set correct pixmap
    if(display_real_note_SBN) {
        ui->preview_note_label->setPixmap(QPixmap(current_note.getPathToImage(current_clef, ":")));
    } else {
        if(interpret_as_real_note) ui->preview_note_label->setPixmap(QPixmap(new_note.getPathToImage(current_clef, ":")));
        else if(!interpret_as_real_note) ui->preview_note_label->setPixmap(QPixmap(old_note.getPathToImage(current_clef, ":")));
    }

    // Update position and position pixmap (has changed)
    updatePosition();
}

void PositionWidget::updatePosition(void)
{
    // Get info and update parser and display
    QString className = ui->class_combo->currentText();
    QString instrumentName = ui->instrument_combo->currentText();
    QString typeName = ui->type_combo->currentText();
    QString tonalityName = ui->tonality_combo->currentText();

    // Get current positions from this configuration

    pos_by_note_list = instrument.getPositions(current_note);
    // OLD STILL WORKING
    //positions = xmlParser.getPositions(className, instrumentName, typeName, tonalityName, note);

    // Display new position
    displayPosition();

    // Freq has changed
    updateFreq();
}

void PositionWidget::updateFreq(void)
{
    // Get temperament
    Temperament temp = toTemperament(ui->temp_combo->currentIndex());

    // Extract frequencies
    qreal note_freq = current_note.getFreq(CONCERT_PITCH_FREQUENCY, Note(CONCERT_PITCH_REFERENCE), temp);
    qreal pos_freq = currentPosition().getFreq();
    //qDebug() << pos_freq;

    // Extract cent difference
    int diff = centsDiff(note_freq, pos_freq);

    // Update spinboxes
    ui->exact_spinbox->setValue(note_freq);
    ui->real_spinbox->setValue(pos_freq);
    ui->difference_spinbox->setValue(diff);

    // Color spinbox
    QPalette* palette = new QPalette();
    if ( qAbs(diff) <= 10 ) {
        palette->setColor(QPalette::Background ,Qt::green);
    } else if ( qAbs(diff) > 10 && qAbs(diff) <= 20 ) {
        palette->setColor(QPalette::Background ,Qt::yellow);
    } else if ( qAbs(diff) > 20 ) {
        palette->setColor(QPalette::Background ,Qt::red);
    }
    ui->difference_spinbox->setPalette(*palette);
}

void PositionWidget::displayPosition(int index)
{
    // Positions to display
    int len = pos_by_note_list.length();
    if (len == 0) {
        current_pos_index = 0;
        QString text = "<b>Positions</b> (%1 of %2):";
        ui->position_label->setText(text.arg(QString::number(0)).arg(QString::number(len)));
        clearPosition();
        return;
    }
    //qDebug() << len;

    // Handle boundaries
    if(index <= -1) {
        current_pos_index = 0;
        displayPosition(0);
        return;
    } else if (index >= len) {
        current_pos_index = len-1;
        displayPosition(len-1);
        return;
    }

    // Update current index
    current_pos_index = index;

    // Change label ("Positions (x of y):")
    QString text = "<b>Positions</b> (%1 of %2):";
    ui->position_label->setText(text.arg(QString::number(index+1)).arg(QString::number(len)));

    // Display position message (if any)
    if(currentPosition().hasMsg()) {
        ui->warning_frame->show();
        ui->warning_label->setText(currentPosition().getMsg());
    } else {
        ui->warning_frame->hide();
        ui->warning_label->setText("");
    }

    displayPositionImg(currentPosition());

    updateFreq();
}

void PositionWidget::clearPosition(void)
{
    displayPositionImg();

}

void PositionWidget::displayPositionImg(Position position)
{
    // Get path info
    QString file = position.getImg();
    QString name = position.getName();
    QString url = ":/images/positions/" + file;
    QResource res(url);

    // Position pixmap or substitute
    QGraphicsScene *scn = new QGraphicsScene( ui->position_view );
    ui->position_view->setScene( scn );
    //qDebug() << url << res.isValid();
    if (res.isValid() && file != "") {
        //ui->position_view->setFixedSize( 100, 100 );
        QPixmap pix( url );
        scn->addPixmap( pix.scaledToHeight(200, Qt::SmoothTransformation) );
    } else {
        if(name == "") name = "Undefined position!";
        QGraphicsTextItem * io = new QGraphicsTextItem;
        //io->setPos(150,70);
        io->setPlainText(name);
        scn->addItem(io);
    }

    // Set position name
    ui->position_name_label->setText("<b>" + name + "</b>");
}

void PositionWidget::displayNote(int index)
{
    // Positions to display
    int len = note_by_pos_list.length();
    if (len == 0) {
        current_note_index = 0;
        QString text = "<b>Notes</b> (0 of 0):";
        clearNote();
        ui->note_label->setText(text);
        ui->note_name_label->setText("-");
        ui->note_view->scene()->clear();
        return;
    }
    //qDebug() << len;

    // Handle boundaries
    if(index <= -1) {
        current_note_index = 0;
        displayNote(0);
        return;
    } else if (index >= len) {
        current_note_index = len-1;
        displayNote(len-1);
        return;
    }

    // Update current index
    current_note_index = index;

    // Change label ("Positions (x of y):")
    QString text = "<b>Positions</b> (%1 of %2):";
    ui->note_label->setText(text.arg(QString::number(index+1)).arg(QString::number(len)));

    displayNoteImg(currentNote().getNote());
}

void PositionWidget::clearNote(void)
{
    displayNoteImg();

}

void PositionWidget::displayNoteImg(Note note)
{
    // Handle tranposition
    Note old_note = note;
    if(!display_real_note_SBP) {
        // Get readed note
        note = instrument.getReadedNote(note);
    }

    // Get path info
    QString name = note.toQString();
    QString url = note.getPathToImage(current_clef);
    QResource res(":" + url);

    // Position pixmap or substitute
    QGraphicsScene *scn = new QGraphicsScene( ui->note_view );
    ui->note_view->setScene( scn );
    //qDebug() << url << res.isValid();
    if (res.isValid() && url != "") {
        //ui->position_view->setFixedSize( 200, 200 );
        QPixmap pix( ":" + url );
        scn->addPixmap( pix );
    } else {
        if(name == "") name = "Undefined note!";
        QGraphicsTextItem * io = new QGraphicsTextItem;
        //io->setPos(150,70);
        io->setPlainText(name);
        scn->addItem(io);
    }

    // Set position name
    QString string1 = tr("<b>%1</b> (real note: <b>%2</b>)");
    QString string2 = tr("<b>%1</b>");
    if(!display_real_note_SBP && !(note == old_note)) {
        ui->note_name_label->setText(string1.arg(name).arg(old_note.toQString()));
    } else {
       ui->note_name_label->setText(string2.arg(name));
    }
}

void PositionWidget::setPitchComboIndex(int i, bool rel)
{
    if(rel == true) {
        int cur = ui->pitchBaseClassCombo->currentIndex();
        if(cur + i < 0 || cur + i >= ui->pitchBaseClassCombo->count()) return;
        ui->pitchBaseClassCombo->setCurrentIndex(cur + i);
    } else {
        if(i < 0 || i >= ui->pitchBaseClassCombo->count()) return;
        ui->pitchBaseClassCombo->setCurrentIndex(i);
    }
}

void PositionWidget::setAltComboIndex(int i, bool rel)
{
    if(rel == true) {
        int cur = ui->pitchAltClassCombo->currentIndex();
        if(cur + i < 0 || cur + i >= ui->pitchAltClassCombo->count()) return;
        ui->pitchAltClassCombo->setCurrentIndex(cur + i);
    } else {
        if(i < 0 || i >= ui->pitchAltClassCombo->count()) return;
        ui->pitchAltClassCombo->setCurrentIndex(i);
    }
}

void PositionWidget::setOctComboIndex(int i, bool rel)
{
    if(rel == true) {
        int cur = ui->pitchOctaveCombo->currentIndex();
        if(cur + i < 0 || cur + i >= ui->pitchOctaveCombo->count()) return;
        ui->pitchOctaveCombo->setCurrentIndex(cur + i);
    } else {
        if(i < 0 || i >= ui->pitchOctaveCombo->count()) return;
        ui->pitchOctaveCombo->setCurrentIndex(i);
    }
}

void PositionWidget::onNoteChanged(void)
{
    updateNote();
}

void PositionWidget::onInstrumentClassChanged(void)
{
    QString className = ui->class_combo->currentText();

    rebuildComboTree(className);

    updateInstrument();
}

void PositionWidget::onInstrumentNameChanged(void)
{
    QString className = ui->class_combo->currentText();
    QString instrumentName = ui->instrument_combo->currentText();

    rebuildComboTree(className, instrumentName);

    updateInstrument();
}

void PositionWidget::onInstrumentTypeChanged(void)
{
    QString className = ui->class_combo->currentText();
    QString instrumentName = ui->instrument_combo->currentText();
    QString typeName = ui->type_combo->currentText();

    rebuildComboTree(className, instrumentName, typeName);

    updateInstrument();
}

void PositionWidget::onInstrumentTonalityChanged(void)
{
    QString className = ui->class_combo->currentText();
    QString instrumentName = ui->instrument_combo->currentText();
    QString typeName = ui->type_combo->currentText();
    QString tonalityName = ui->tonality_combo->currentText();

    rebuildComboTree(className, instrumentName, typeName, tonalityName);

    updateInstrument();
}

void PositionWidget::onClefChanged(void)
{
    if (ui->treble_radio_button->isChecked()) current_clef = Clef(0);
    else if (ui->tenor_radio_button->isChecked()) current_clef = Clef(1);
    else if (ui->bass_radio_button->isChecked()) current_clef = Clef(2);

    updateSearchByPosition();
    updateSearchByNote();
}

void PositionWidget::updateSearchByPosition()
{
    QString basepos = ui->positions_list_combo->currentText();

    note_by_pos_list = instrument.getPositions(basepos);

    Position pos = currentNote();

    // Get path info
    QString file = pos.getImg();
    QString name = pos.getImg();
    QString url = ":/images/positions/" + file;
    //qDebug() << "Using url:" << url;
    QResource res(url);

    // Position pixmap or substitute
    if (res.isValid() && file != "") {
        QPixmap pix( url );
        ui->preview_position_label->setPixmap( pix.scaledToHeight(200, Qt::SmoothTransformation) );
    } else {
        if(name == "") name = "Undefined position!";
        ui->preview_position_label->setText(name);
    }

    displayNote(0);
}

void PositionWidget::onInstrumentButtonClicked()
{
    diag = new QDialog();
    diag->setMinimumWidth(700);
    inswid = new InstrumentInfo(instrument);

    QVBoxLayout * lay = new QVBoxLayout();

    lay->addWidget(inswid);

    diag->setLayout(lay);
    diag->show();

}
